/**
 * Copyright (c) 2006-2015, JGraph Ltd
 * Copyright (c) 2006-2015, Gaudenz Alder
 */
/**
 * Class: mxXmlRequest
 *
 * XML HTTP request wrapper. See also: <mxUtils.get>, <mxUtils.post> and
 * <mxUtils.load>. This class provides a cross-browser abstraction for Ajax
 * requests.
 *
 * Encoding:
 *
 * For encoding parameter values, the built-in encodeURIComponent JavaScript
 * method must be used. For automatic encoding of post data in <mxEditor> the
 * <mxEditor.escapePostData> switch can be set to true (default). The encoding
 * will be carried out using the conte type of the page. That is, the page
 * containting the editor should contain a meta tag in the header, eg.
 * <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 *
 * Example:
 *
 * (code)
 * var onload = function(req)
 * {
 *   mxUtils.alert(req.getDocumentElement());
 * }
 *
 * var onerror = function(req)
 * {
 *   mxUtils.alert('Error');
 * }
 * new mxXmlRequest(url, 'key=value').send(onload, onerror);
 * (end)
 *
 * Sends an asynchronous POST request to the specified URL.
 *
 * Example:
 *
 * (code)
 * var req = new mxXmlRequest(url, 'key=value', 'POST', false);
 * req.send();
 * mxUtils.alert(req.getDocumentElement());
 * (end)
 *
 * Sends a synchronous POST request to the specified URL.
 *
 * Example:
 *
 * (code)
 * var encoder = new mxCodec();
 * var result = encoder.encode(graph.getModel());
 * var xml = encodeURIComponent(mxUtils.getXml(result));
 * new mxXmlRequest(url, 'xml='+xml).send();
 * (end)
 *
 * Sends an encoded graph model to the specified URL using xml as the
 * parameter name. The parameter can then be retrieved in C# as follows:
 *
 * (code)
 * string xml = HttpUtility.UrlDecode(context.Request.Params["xml"]);
 * (end)
 *
 * Or in Java as follows:
 *
 * (code)
 * String xml = URLDecoder.decode(request.getParameter("xml"), "UTF-8").replace("\n", "&#xa;");
 * (end)
 *
 * Note that the linefeeds should only be replaced if the XML is
 * processed in Java, for example when creating an image.
 *
 * Constructor: mxXmlRequest
 *
 * Constructs an XML HTTP request.
 *
 * Parameters:
 *
 * url - Target URL of the request.
 * params - Form encoded parameters to send with a POST request.
 * method - String that specifies the request method. Possible values are
 * POST and GET. Default is POST.
 * async - Boolean specifying if an asynchronous request should be used.
 * Default is true.
 * username - String specifying the username to be used for the request.
 * password - String specifying the password to be used for the request.
 */
function mxXmlRequest(url, params, method, async, username, password) {
  this.url = url;
  this.params = params;
  this.method = method || "POST";
  this.async = async != null ? async : true;
  this.username = username;
  this.password = password;
}

/**
 * Variable: url
 *
 * Holds the target URL of the request.
 */
mxXmlRequest.prototype.url = null;

/**
 * Variable: params
 *
 * Holds the form encoded data for the POST request.
 */
mxXmlRequest.prototype.params = null;

/**
 * Variable: method
 *
 * Specifies the request method. Possible values are POST and GET. Default
 * is POST.
 */
mxXmlRequest.prototype.method = null;

/**
 * Variable: async
 *
 * Boolean indicating if the request is asynchronous.
 */
mxXmlRequest.prototype.async = null;

/**
 * Variable: binary
 *
 * Boolean indicating if the request is binary. This option is ignored in IE.
 * In all other browsers the requested mime type is set to
 * text/plain; charset=x-user-defined. Default is false.
 */
mxXmlRequest.prototype.binary = false;

/**
 * Variable: withCredentials
 *
 * Specifies if withCredentials should be used in HTML5-compliant browsers. Default is
 * false.
 */
mxXmlRequest.prototype.withCredentials = false;

/**
 * Variable: username
 *
 * Specifies the username to be used for authentication.
 */
mxXmlRequest.prototype.username = null;

/**
 * Variable: password
 *
 * Specifies the password to be used for authentication.
 */
mxXmlRequest.prototype.password = null;

/**
 * Variable: request
 *
 * Holds the inner, browser-specific request object.
 */
mxXmlRequest.prototype.request = null;

/**
 * Variable: decodeSimulateValues
 *
 * Specifies if request values should be decoded as URIs before setting the
 * textarea value in <simulate>. Defaults to false for backwards compatibility,
 * to avoid another decode on the server this should be set to true.
 */
mxXmlRequest.prototype.decodeSimulateValues = false;

/**
 * Function: isBinary
 *
 * Returns <binary>.
 */
mxXmlRequest.prototype.isBinary = function () {
  return this.binary;
};

/**
 * Function: setBinary
 *
 * Sets <binary>.
 */
mxXmlRequest.prototype.setBinary = function (value) {
  this.binary = value;
};

/**
 * Function: getText
 *
 * Returns the response as a string.
 */
mxXmlRequest.prototype.getText = function () {
  return this.request.responseText;
};

/**
 * Function: isReady
 *
 * Returns true if the response is ready.
 */
mxXmlRequest.prototype.isReady = function () {
  return this.request.readyState == 4;
};

/**
 * Function: getDocumentElement
 *
 * Returns the document element of the response XML document.
 */
mxXmlRequest.prototype.getDocumentElement = function () {
  var doc = this.getXml();

  if (doc != null) {
    return doc.documentElement;
  }

  return null;
};

/**
 * Function: getXml
 *
 * Returns the response as an XML document. Use <getDocumentElement> to get
 * the document element of the XML document.
 */
mxXmlRequest.prototype.getXml = function () {
  var xml = this.request.responseXML;

  // Handles missing response headers in IE, the first condition handles
  // the case where responseXML is there, but using its nodes leads to
  // type errors in the mxCellCodec when putting the nodes into a new
  // document. This happens in IE9 standards mode and with XML user
  // objects only, as they are used directly as values in cells.
  if (
    document.documentMode >= 9 ||
    xml == null ||
    xml.documentElement == null
  ) {
    xml = mxUtils.parseXml(this.request.responseText);
  }

  return xml;
};

/**
 * Function: getText
 *
 * Returns the response as a string.
 */
mxXmlRequest.prototype.getText = function () {
  return this.request.responseText;
};

/**
 * Function: getStatus
 *
 * Returns the status as a number, eg. 404 for "Not found" or 200 for "OK".
 * Note: The NS_ERROR_NOT_AVAILABLE for invalid responses cannot be cought.
 */
mxXmlRequest.prototype.getStatus = function () {
  return this.request.status;
};

/**
 * Function: create
 *
 * Creates and returns the inner <request> object.
 */
mxXmlRequest.prototype.create = (function () {
  if (window.XMLHttpRequest) {
    return function () {
      var req = new XMLHttpRequest();

      // TODO: Check for overrideMimeType required here?
      if (this.isBinary() && req.overrideMimeType) {
        req.overrideMimeType("text/plain; charset=x-user-defined");
      }

      return req;
    };
  } else if (typeof ActiveXObject != "undefined") {
    return function () {
      // TODO: Implement binary option
      return new ActiveXObject("Microsoft.XMLHTTP");
    };
  }
})();

/**
 * Function: send
 *
 * Send the <request> to the target URL using the specified functions to
 * process the response asychronously.
 *
 * Note: Due to technical limitations, onerror is currently ignored.
 *
 * Parameters:
 *
 * onload - Function to be invoked if a successful response was received.
 * onerror - Function to be called on any error.
 * timeout - Optional timeout in ms before calling ontimeout.
 * ontimeout - Optional function to execute on timeout.
 */
mxXmlRequest.prototype.send = function (onload, onerror, timeout, ontimeout) {
  this.request = this.create();

  if (this.request != null) {
    if (onload != null) {
      this.request.onreadystatechange = mxUtils.bind(this, function () {
        if (this.isReady()) {
          onload(this);
          this.request.onreadystatechaange = null;
        }
      });
    }

    this.request.open(
      this.method,
      this.url,
      this.async,
      this.username,
      this.password
    );
    this.setRequestHeaders(this.request, this.params);

    if (window.XMLHttpRequest && this.withCredentials) {
      this.request.withCredentials = "true";
    }

    if (
      !mxClient.IS_QUIRKS &&
      (document.documentMode == null || document.documentMode > 9) &&
      window.XMLHttpRequest &&
      timeout != null &&
      ontimeout != null
    ) {
      this.request.timeout = timeout;
      this.request.ontimeout = ontimeout;
    }

    this.request.send(this.params);
  }
};

/**
 * Function: setRequestHeaders
 *
 * Sets the headers for the given request and parameters. This sets the
 * content-type to application/x-www-form-urlencoded if any params exist.
 *
 * Example:
 *
 * (code)
 * request.setRequestHeaders = function(request, params)
 * {
 *   if (params != null)
 *   {
 *     request.setRequestHeader('Content-Type',
 *             'multipart/form-data');
 *     request.setRequestHeader('Content-Length',
 *             params.length);
 *   }
 * };
 * (end)
 *
 * Use the code above before calling <send> if you require a
 * multipart/form-data request.
 */
mxXmlRequest.prototype.setRequestHeaders = function (request, params) {
  if (params != null) {
    request.setRequestHeader(
      "Content-Type",
      "application/x-www-form-urlencoded"
    );
  }
};

/**
 * Function: simulate
 *
 * Creates and posts a request to the given target URL using a dynamically
 * created form inside the given document.
 *
 * Parameters:
 *
 * docs - Document that contains the form element.
 * target - Target to send the form result to.
 */
mxXmlRequest.prototype.simulate = function (doc, target) {
  doc = doc || document;
  var old = null;

  if (doc == document) {
    old = window.onbeforeunload;
    window.onbeforeunload = null;
  }

  var form = doc.createElement("form");
  form.setAttribute("method", this.method);
  form.setAttribute("action", this.url);

  if (target != null) {
    form.setAttribute("target", target);
  }

  form.style.display = "none";
  form.style.visibility = "hidden";

  var pars =
    this.params.indexOf("&") > 0 ? this.params.split("&") : this.params.split();

  // Adds the parameters as textareas to the form
  for (var i = 0; i < pars.length; i++) {
    var pos = pars[i].indexOf("=");

    if (pos > 0) {
      var name = pars[i].substring(0, pos);
      var value = pars[i].substring(pos + 1);

      if (this.decodeSimulateValues) {
        value = decodeURIComponent(value);
      }

      var textarea = doc.createElement("textarea");
      textarea.setAttribute("wrap", "off");
      textarea.setAttribute("name", name);
      mxUtils.write(textarea, value);
      form.appendChild(textarea);
    }
  }

  doc.body.appendChild(form);
  form.submit();

  if (form.parentNode != null) {
    form.parentNode.removeChild(form);
  }

  if (old != null) {
    window.onbeforeunload = old;
  }
};
